import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Queue of URLs that have yet to be retrieved.
 */
public class PageToRetrieve {
	// INSTANCE FIELDS **********************************************
	/**
	 * Thread safe Linked List backed Blocking Queue holding urls.
	 */
	private ConcurrentLinkedQueue<String> urls;

	/**
	 * Hashtable that tracks whether we have seen a link or not.
	 */
	private Hashtable<String, Boolean> seen;

	/**
	 * What retriever threads do we have running right now?
	 */
	private ArrayList<Thread> threads;

	/**
	 * Reference to the PageBuffer instantiation we're working with.
	 */
	private PageBuffer pbuff;

	/**
	 * Is the instance supposed to have more than one thread?
	 */
	private boolean isThreaded;

	/**
	 * How many pages have we retrieved so far?
	 */
	private int pagesRetrieved;

	/**
	 * Did the threaded mode get set?
	 */
	private boolean isThreadedSet;

	/**
	 * Did the seed get set?
	 */
	private boolean isSeedSet;

	/**
	 * Parent GUIFrame that we're working with.
	 */
	private GUIFrame parent;

	/**
	 * List of the current KeyWords.
	 */
	private List<String> keywords;

	/**
	 * Number of pages to retrieve in total.
	 */
	private int pageCap;

	/**
	 * creates a PageToRetrieve queue and adds the seed url.
	 * 
	 * @param seed
	 *            url where the spider will begin retrieving links
	 */
	public PageToRetrieve(GUIFrame parent) {
		urls = new ConcurrentLinkedQueue<String>();
		seen = new Hashtable<String, Boolean>();
		threads = new ArrayList<Thread>();

		this.parent = parent;
		pbuff = new PageBuffer(this);
		threads.add((new Thread(new PageRetriever(pbuff, this))));
	}

	/**
	 * Runs one cycle of this programs behavior. This method calls the PageBuffer's tick method automatically.
	 */
	public void tick() {
		// must init these two things
		if (!isThreadedSet || !isSeedSet)
			return;
		if ((urls.size() % 5 == 0 && threads.size() < 11) && isThreaded) {
			threads.add((new Thread(new PageRetriever(pbuff, this))));
		}

		for (Thread t : threads) {
			if (pagesRetrieved < pageCap) {
				t.run();
			}
		}
		pbuff.tick();
	}

	/**
	 * Accessor to set the number of pages to retrieve.
	 * @param depth How many pages should we retrieve?
	 */
	public void setPageCap(int depth) {
		pageCap = depth;
		pbuff.DataGather().setPageCap(depth);
	}
	
	/**
	 * @return What is the pageCap for this session.
	 */
	public int pageCap() {
		return pageCap;
	}

	/**
	 * @return How many pages we have retrieved so far.
	 */
	public int pagesRetrieved() {
		return pagesRetrieved;
	}

	/**
	 * Set the threaded mode of this application.
	 * @param threaded True for multithreaded, false for single threaded.
	 */
	public void setThreaded(boolean threaded) {
		isThreaded = threaded;
		pbuff.setThreaded(threaded);
		isThreadedSet = true;
	}

	/**
	 * Set the URL that we will start at.
	 * @param url URL to start at.
	 */
	public void setSeed(String url) {
		urls.add(url);
		isSeedSet = true;
	}

	/**
	 * @return Underlying queue for this class.
	 */
	public ConcurrentLinkedQueue<String> queue() {
		return urls;
	}

	/**
	 * Increments the number of pages we've retrieved.
	 */
	public void incrementPageCount() {
		pagesRetrieved++;
	}

	/**
	 * Sets the list of keywords.
	 * @param the_keywords List containing the keywords to look for.
	 */
	public void setKeywords(List<String> the_keywords) {
		keywords = the_keywords;
		pbuff.setKeys(the_keywords);
	}

	/**
	 * returns the keyword list.
	 * @return List<String> keywords
	 */
	public List<String> getKeywords() {
		return keywords;
	}

	/**
	 * @return The PageBuffer that this class is working with.
	 */
	public PageBuffer pageBuffer() {
		return pbuff;
	}

	/**
	 * @return The parent of this class.
	 */
	public GUIFrame getParent() {
		return parent;
	}

	/**
	 * @param url URL to check.
	 * @return Have we already seen this URL?
	 */
	public synchronized boolean seenURL(String url) {
		if (seen.get(url) == null || seen.get(url) == false) {
			setSeen(url);
			return false;
		}
		return true;
	}

	/**
	 * Marks the URL as seen.
	 * @param url URL to mark.
	 */
	public synchronized void setSeen(String url) {
		seen.put(url, true);
	}

	/**
	 * @return Has the seed been set?
	 */
	public boolean hasSeed() {
		return isSeedSet;
	}

	/**
	 * @return Is there another element in the queue?
	 */
	public boolean hasNext() {
		return urls.peek() != null ? true : false;
	}
}
